/*
what is socket:
	socket是使用 标准Unix 文件描述符 (file descriptor) 和其它程序通讯的方式。
	unix 程序在执行任何形式的 I/O 的时候，程序是在读或者写一个文件描述符。一个文件描述符只是一个和打开的文件相关联的整数。这个文件可能是一个网络连接，FIFO，管道，终端，磁盘上的文件或者什么其它的东西。
	you can use 系统调用 socket()，它返回套接字描述符 (socket descriptor)，然后你再通过它来进行send() 和 recv()调用。

	不要采用小于 1024的端口号。所有小于1024的端口号都被系统保留 你可以选择从1024 到65535的端口
*/

#include <cstdio>
#include <cstdlib>
#include <string.h>
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h> 
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>

// client test
int tcpClientTest(int argc, char** argv)
{
	std::cout<<" in func tcpClientTest"<<std::endl;

	int sfd = socket(AF_INET, SOCK_STREAM, 0);
	if(-1 == sfd)
	{
		perror("fail to create socket file descriptor");
		return -1;
	}

	const short portnum = 1226;
	struct sockaddr_in saddr;
	saddr.sin_family = AF_INET;
	saddr.sin_port = htons(portnum);
	saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
	bzero(&(saddr.sin_zero), sizeof(saddr.sin_zero));

	int cnnre = connect(sfd, (struct sockaddr*)&saddr, sizeof(struct sockaddr));
	if(-1 == cnnre)
	{
		perror("fail to connect");
		return -1;
	}

	char* msg2remote = "how are u?";
	int msglen = strlen(msg2remote);
	int bytesout = send(sfd, msg2remote, msglen, 0);
	if(-1 == bytesout)
	{
		perror("fail to call send to tell remote 'service alive'");
		return -1;
	}
	std::cout<<bytesout<<" bytes rsp send "<<std::endl;

	char buf[1024];
	bzero(buf,1024);
	int rcfre = recv(sfd, buf, 1024, 0);
	if(-1 == rcfre)
	{
		perror("fail to call recv");
		return -1;
	}
	std::cout<<"recv:"<<buf<<std::endl;

	close(sfd);

	return 0;
}

int udpClientTest(int argc, char** argv)
{
	std::cout<<" in func udpClientTest"<<std::endl;

	int sfd = socket(AF_INET, SOCK_DGRAM, 0);
	if(-1 == sfd)
	{
		perror("fail to create socket file descriptor");
		return -1;
	}

	const short portnum = 1225;
	struct sockaddr_in saddr;
	saddr.sin_family = AF_INET;
	saddr.sin_port = htons(portnum);
	saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
	bzero(&(saddr.sin_zero), sizeof(saddr.sin_zero));

	/* when using upd connect call is not necessary
	int cnnre = connect(sfd, (struct sockaddr*)&saddr, sizeof(struct sockaddr));
	if(-1 == cnnre)
	{
		perror("fail to connect");
		return -1;
	}
	//*/

	char* msg2remote = "how are u?";
	int msglen = strlen(msg2remote);
	int bytesout = sendto(sfd, msg2remote, msglen, 0, (struct sockaddr*)&saddr, sizeof(struct sockaddr));
	if(-1 == bytesout)
	{
		perror("fail to call send to tell remote 'service alive'");
		return -1;
	}
	std::cout<<bytesout<<" bytes rsp send "<<std::endl;

	char buf[1024];
	bzero(buf,1024);
	int addr_len = sizeof(struct sockaddr);
	int rcfre = recvfrom(sfd, buf, 1024, 0, (struct sockaddr*)&saddr, (socklen_t*)&addr_len);
	if(-1 == rcfre)
	{
		perror("fail to call recv");
		return -1;
	}
	std::cout<<"recv:"<<buf<<std::endl;

	close(sfd);

	return 0;
}

int main(int argc, char** argv)
{
	if(argc>1)
	{
		int testre = tcpClientTest(argc, argv);
		std::cout<<"tcpClientTest return "<<testre<<std::endl;
	}
	else
	{
		int testre = udpClientTest(argc, argv);
		std::cout<<"udpClientTest return "<<testre<<std::endl;
	}
	return 0;
}
